﻿using Application.Commons.Data;
using AutoMapper;
using FrameworkCore.Helpers;
using FrameworkCore.Models;
using MediatR;
using Microsoft.EntityFrameworkCore;
using NM.Module.Auth.API.DTOs;
using NM.Module.Auth.API.Interfaces;
using System.ComponentModel.DataAnnotations;
using System.Security.Claims;

namespace NM.Module.Auth.API.Commands.Login;

#nullable disable
/// <summary>
/// 密码登录
/// </summary>
public class LoginCommand : IRequest<Result<LoginOutputModel>>
{
    /// <summary>
    /// 用户名
    /// </summary>
    [Required(ErrorMessage = "用户名是必填的")]
    public string Username { get; set; }

    /// <summary>
    /// 密码
    /// </summary>
    [Required(ErrorMessage = "密码是必填的")]
    public string Password { get; set; }
}

public class LoginCommandHandler : IRequestHandler<LoginCommand, Result<LoginOutputModel>>
{
    private readonly ApplicationDbContext _context;
    private readonly IMapper _mapper;
    private readonly ITokenService _tokenService;

    public LoginCommandHandler(
        ApplicationDbContext context,
        IMapper mapper, ITokenService
        tokenService)
    {
        _context = context;
        _mapper = mapper;
        _tokenService = tokenService;
    }

    public async Task<Result<LoginOutputModel>> Handle(LoginCommand request, CancellationToken cancellationToken)
    {
        var customer = await _context.Customers.SingleOrDefaultAsync(x => x.Username == request.Username);

        ExceptionHelper.ThrowIfNull(customer, "用户名或密码错误");
        ExceptionHelper.ThrowIfFalse(customer.VerifyPassword(request.Password), "用户名或密码错误");

        List<Claim> claims = new()
        {
            new Claim(ClaimTypes.NameIdentifier, customer.Id.ToString()),
            new Claim(ClaimTypes.Name, customer.Username)
        };

        int expires = 0;
        var result = _mapper.Map<LoginOutputModel>(customer);

        (result.RefreshToken, expires) = await _tokenService.BuildAsync(claims, null, true);
        (result.AccessToken, expires) = await _tokenService.BuildAsync(claims);
        result.Expires = DateTime.Now.AddSeconds(expires).ToString("yyyy/MM/dd HH:mm:ss");

        return await Result<LoginOutputModel>.SucceedAsync(result);
    }
}